#pragma once
#include <iostream>
#include <cstdint>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <thread>
#include <functional>
#include <memory>
#include <fcntl.h>
using namespace std;

#define default_backlog 5

// 设计模式：模版方法类
class my_socket // 抽象类
{
public:
    virtual void Creat_socket() = 0; // 纯虚函数,必须重写
    virtual void Reuse() = 0;
    virtual void Bind(int port) = 0;
    virtual void Listen(int backlog) = 0;
    virtual void NonBlock(int sockfd) = 0;
    virtual my_socket *Accept(string &ip, uint16_t &port) = 0;
    virtual void Connect(string ip, uint16_t port) = 0;
    virtual int Get_sockfd() = 0;
    virtual void Close() = 0;

    virtual bool Recv(string &ret, int len) = 0;
    virtual void Send(string s) = 0;

public:
    void tcpserver_socket(uint16_t port, int backlog = default_backlog)
    {
        Creat_socket();
        Reuse();
        Bind(port);
        Listen(backlog);
        // 因为服务会返回的执行accept获取连接，所以选择分离
    }
    void tcpclient_socket(string ip, uint16_t port)
    {
        Creat_socket();
        Connect(ip, port);
    }
};

class tcp_socket : public my_socket // 继承并重写虚函数
{
public:
    tcp_socket(int sockfd = 0)
        : _sockfd(sockfd) {}

    virtual void Creat_socket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            cerr << "创建套接字失败" << endl;
            exit(-1);
        }
    }
    void Reuse()
    {
        // 设置套接字选项以启用端口复用
        int opt = 1;
        if (setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
        {
            perror("setsockopt");
            exit(EXIT_FAILURE);
        }
    }
    virtual void Bind(int port)
    {
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_sockfd, (sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            cerr << "绑定套接字失败" << endl;
            exit(-1);
        }
    }
    virtual void Listen(int backlog)
    {
        int n = listen(_sockfd, backlog);
        if (n == -1)
        {
            cerr << "监听套接字失败" << endl;
            exit(-1);
        }
    }
    void NonBlock(int sockfd)
    {
        // 设置套接字为非阻塞模式
        int flags = fcntl(sockfd, F_GETFL, 0);
        fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    }
    virtual my_socket *Accept(string &ip, uint16_t &port)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int newsockfd = accept(_sockfd, (sockaddr *)&client, &len); // 监听套接字不关闭，可以用来接收多个客户端的连接
        if (newsockfd < 0)
        {
            cerr << strerror(errno) << " 获取连接失败" << endl;
            return nullptr;
        }

        // 通信套接字设置非阻塞
        // NonBlock(newsockfd);

        port = ntohs(client.sin_port);
        char buffer[64];
        inet_ntop(AF_INET, &client.sin_addr, buffer, sizeof(buffer)); // 1.网络转本机 2.4字节ip转字符串ip
        ip = buffer;

        if (newsockfd < 0)
        {
            cerr << "接收套接字失败" << endl;
        }
        else
            cout << "接收套接字成功" << endl;

        return new tcp_socket(newsockfd); // 基类指针指向派生类对象
    }
    virtual void Connect(string ip, uint16_t port)
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;                      // socket inet(ip) 协议家族,绑定网络通信的信息
        server.sin_port = htons(port);                    // 将主机端口号转成网络
        inet_pton(AF_INET, ip.c_str(), &server.sin_addr); // 转成网络序列的四字节ip

        int n = connect(_sockfd, (sockaddr *)&server, sizeof(server)); // 自动bind
        if (n != 0)
        {
            cerr << "连接失败" << endl;
            exit(-1);
        }
        else
            cout << "连接成功" << endl;
    }

    virtual int Get_sockfd()
    {
        return _sockfd;
    }

    virtual void Close()
    {
        if (_sockfd > 0)
            close(_sockfd);
    }

    virtual bool Recv(string &ret, int len)
    {
        char stream_buffer[len];
        int n = recv(_sockfd, stream_buffer, len - 1, 0);

        if (n > 0)
        {
            stream_buffer[n] = 0;
            ret = stream_buffer; // ret在读取之前可能还有内容残留
            return true;
        }
        else if (n==-1)
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK)
            {
                cout<<"数据读取中...."<<endl;
            }
            return false;
        }
        else // n=0表示客户端关闭连接
        {
            close(_sockfd);
            pthread_exit(nullptr);
            return false;
            // exit(0);会终止整个进程
        }
    }

    virtual void Send(string s)
    {
        send(_sockfd, s.c_str(), s.size(), 0);
    }

private:
    int _sockfd;
};
